<?php
/**********************************************************************************
WIKINDX: Bibliographic Management system.
Copyright (C)

This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program;
if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

The WIKINDX Team 2008
sirfragalot@users.sourceforge.net
**********************************************************************************/
/*****
* HTML widgets class
* 
* $Header: /cvsroot/wikindx/wikindx3/core/html/HTML.php,v 1.13 2008/02/02 12:04:38 sirfragalot Exp $
*****/
class HTML
{
// constructor
	function HTML()
	{
	}
// Strip all BBCode tags
	function stripBBCode($string)
	{
		return preg_replace("/\[.*\]|\[\/.*\]/U", "", $string);
	}
// replace [x]...[/x] BBcode with HTML code
// Used for display back from DB table
	function codeToHtml($string)
	{
// First things first: If there aren't any "[*" or hyperlinks strings in the message, we don't
// need to process it at all.
		if (!preg_match("/\[.*|www\.|ftp\./", $string))
			return $string;
// Get BBCode image display limits
		include_once("core/session/SESSION.php");
		$this->session = new SESSION();
		$this->imgWidthLimit = $this->session->getVar("config_imgWidthLimit");
		$this->imgHeightLimit = $this->session->getVar("config_imgHeightLimit");
// code - this must be parsed first so that other BBCode within [code]...[/code] can be safely removed.
		$string = preg_replace_callback("/\[code\](.*)\[\/code\]/is", 
			array($this, 'codeCallback'), $string);
		$pattern = array(
							"/\[b\](.*?)\[\/b\]/si",
							"/\[u\](.*?)\[\/u\]/si",
							"/\[i\](.*?)\[\/i\]/si",
							"/\[sup\](.*?)\[\/sup\]/si",
							"/\[sub\](.*?)\[\/sub\]/si",
							"/\[size=(.*?)\](.*?)\[\/size\]/is",
							"/\[color=(.*?)\](.*?)\[\/color\]/is",
							"/\[float=(.*?)\](.*?)\[\/float\]/is",
//							"@(www([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@is",
//							"@(ftp([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@is",
//							"@((https?|ftp)://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@is",
							"/\[url\](.*?)\[\/url\]/is",
							"/\[url=(.*?)\](.*?)\[\/url\]/is",
							"/\[email\](.*?)\[\/email\]/is",
							"/\[img\](.*?)\[\/img\]/is",
							"/\[align=(.*?)\](.*?)\[\/align\]/is",
						);
		$change = array(
							"<strong>$1</strong>",
							"<span style=\"text-decoration: underline;\">$1</span>",
							"<em>$1</em>",
							"<sup>$1</sup>",
							"<sub>$1</sub>",
							"<span style=\"font-size: $1px;\">$2</span>",
							"<span style=\"color: $1;\">$2</span>",
							"<span style=\"float: $1; margin: 0.25em\" display: inline>$2</span>",
//							"<a class=\"link\" href=\"http://\\1\" target=\"_blank\">\\1</a>",
//							"<a class=\"link\" href=\"ftp://\\1\" target=\"_blank\">\\1</a>",
							"<a class=\"link\" href=\"$1\" target=\"_blank\">$1</a>",
							"<a class=\"link\" href=\"$1\" target=\"_blank\">$2</a>",
							"<a class=\"link\" href=\"mailto:$1\">$1</a>",
							"<img src=\"$1\" border=\"0\" />",
							"<div align=\"$1\">$2</div>",
						);
		$string = preg_replace($pattern, $change, $string);
// image + dimensions
		$string = preg_replace_callback("/\[img=(.*)\*(.*)\](.*?)\[\/img\]/is",
			array($this, 'imgCallback'), $string);
// bbencode_list requires initial ' ' - remove it after processing.
		return substr($this->bbencode_list(' ' . $string), 1);
	}
// Callback function for [code]...[/code] which cannot have other BBCode within it
	function codeCallback($matches)
	{
		return "<code>" . preg_replace("#\[.*\]+(.+?)\[/.*\]*#s", "$1", $matches[1]) . "</code>";
	}
// Callback function for [img=width*height]...[/img] to check display limitations
	function imgCallback($matches)
	{
		$width = ($matches[1] > $this->imgWidthLimit) ? $this->imgWidthLimit : $matches[1];
		$height = ($matches[2] > $this->imgHeightLimit) ? $this->imgHeightLimit : $matches[2];
		return "<img src=\"$matches[3]\" width=\"$width\" height=\"$height\" border=\"0\" />";
	}
// Decode [list]...[/list] and [list=xx]...[/list]
// This has been shamelessly pinched from PHP Bulletin Board code with a little debugging....
/**
 * Nathan Codding - Jan. 12, 2001.
 * Performs [list][/list] and [list=?][/list] bbencoding on the given string, and returns the results.
 * Any unmatched "[list]" or "[/list]" token will just be left alone. 
 * This works fine with both having more than one list in a message, and with nested lists.
 * Since that is not a regular language, this is actually a PDA and uses a stack. Great fun.
 *
 * Note: This function assumes the first character of $message is a space, which is added by 
 * bbencode().
 */
function bbencode_list($message)
{		
	$start_length = Array();
	$start_length['ordered'] = 8;
	$start_length['unordered'] = 6;
	
	// First things first: If there aren't any "[list" strings in the message, we don't
	// need to process it at all.
	
	if(strpos(strtolower($message), "[list") === FALSE)
	{
		return $message;	
	}
	$stack = Array();
	$curr_pos = 1;
	while ($curr_pos && ($curr_pos < strlen($message)))
	{	
		$curr_pos = strpos($message, "[", $curr_pos);
	
		// If not found, $curr_pos will be 0, and the loop will end.
		if ($curr_pos)
		{
			// We found a [. It starts at $curr_pos.
			// check if it's a starting or ending list tag.
			$possible_ordered_start = substr($message, $curr_pos, $start_length['ordered']);
			$possible_unordered_start = substr($message, $curr_pos, $start_length['unordered']);
			$possible_end = substr($message, $curr_pos, 7);
			if (strcasecmp("[list]", $possible_unordered_start) == 0)
			{
				// We have a starting unordered list tag.
				// Push its position on to the stack, and then keep going to the right.
				array_push($stack, array($curr_pos, ""));
				++$curr_pos;
			}
			else if (preg_match("/\[list=([a1i])\]/si", $possible_ordered_start, $matches))
			{
				// We have a starting ordered list tag.
				// Push its position on to the stack, and the starting char onto the start
				// char stack, the keep going to the right.
				array_push($stack, array($curr_pos, $matches[1]));
				++$curr_pos;
			}
			else if (strcasecmp("[/list]", $possible_end) == 0)
			{
				// We have an ending list tag.
				// Check if we've already found a matching starting tag.
				if (sizeof($stack) > 0)
				{
					// There exists a starting tag. 
					// We need to do 2 replacements now.
					$start = array_pop($stack);
					$start_index = $start[0];
					$start_char = $start[1];
					$is_ordered = ($start_char != "");
					$start_tag_length = ($is_ordered) ? $start_length['ordered'] : $start_length['unordered'];
					// everything before the [list] tag.
					$before_start_tag = substr($message, 0, $start_index);
					// everything after the [list] tag, but before the [/list] tag.
					$between_tags = substr($message, $start_index + $start_tag_length, 
						$curr_pos - $start_index - $start_tag_length);
					// Need to replace [*] with <LI> inside the list.
					$between_tags = str_replace("[*]", "<li>", $between_tags);					
					// everything after the [/list] tag.
					$after_end_tag = substr($message, $curr_pos + 7);

					if ($is_ordered)
					{
						$message = $before_start_tag . "<ol TYPE=" . $start_char . ">";
						$message .= $between_tags . "</ol>";
					}
					else
					{
						$message = $before_start_tag . "<ul>";
						$message .= $between_tags . "</ul>";
					}					
					$message .= $after_end_tag;					
					// Now.. we've screwed up the indices by changing the length of the string. 
					// So, if there's anything in the stack, we want to resume searching just after it.
					// otherwise, we go back to the start.
					if (sizeof($stack) > 0)
					{
						$a = array_pop($stack);
						$curr_pos = $a[0];
						array_push($stack, $a);
						++$curr_pos;
					}
					else
					{
						$curr_pos = 1;
					}
				}
				else
				{
					// No matching start tag found. Increment pos, keep going.
					++$curr_pos;	
				}
			}
			else
			{
				// No starting tag or ending tag.. Increment pos, keep looping.,
				++$curr_pos;	
			}
		}
	} // while	
	return $message;
	
} // bbencode_list()

// replace newlines and carriage returns with appropriate HTML code.
// first multiples then singles.
// Used for display back from DB table
	function nlToHtml($string)
	{
		$string = preg_replace("/(\015?\012){2,}/", "<br /><br />", $string);
		return preg_replace("/(\015?\012){1,}/", "<br />\n", $string);
	}
// remove all newlines.  For cases when user cut 'n' pastes multiple lines into single-line text box
// Used before writing to DB table
	function removeNl($string)
	{
		$string = preg_replace("/(\015?\012){2,}/", " ", $string);
		return preg_replace("/\r|\n/", " ", $string);
	}
// Format text grabbed from database for printing to browser. Don't use for form elements.
	function dbToHtmlTidy($string)
	{
		$string =  htmlspecialchars(stripslashes($string), ENT_QUOTES);
		$string = $this->codeToHtml($string);
		return $this->nlToHtml($string);
	}
// Format text grabbed from database for printing to form elements.
	function dbToFormTidy($string)
	{
		return  htmlspecialchars(stripslashes($string), ENT_QUOTES);
	}
}
?>
